package com.vinjogames.gfx;

import android.graphics.PointF;

import com.vinjogames.entities.Game;
import com.vinjogames.entities.GameObject;
import com.vinjogames.entities.Projectile;
import com.vinjogames.util.GameClock;
import com.vinjogames.util.GameConVars;

public class Camera {
	public static float GL_WIDTH  = 10;
	public static float GL_HEIGHT = 6;
	public static int SCREEN_WIDTH;
	public static int SCREEN_HEIGHT;
	public static float SCREEN_RATIO;

	public static final float FOVY         = 45;
	public static final float DISTANCE_MOD = (float)Math.tan(Math.toRadians(FOVY / 2));
	public static final float Z_DISTANCE   = -(3/DISTANCE_MOD);
	public static final float MAXIMUM_VIEW = 3;
	public static final float MINIMUM_VIEW = (1f/3f);

	private float mLeft;
	private float mBottom;
	private float mScale;

	private float mLeftBound;
	private float mRightBound;
	private float mTopBound;
	private float mBottomBound;
	
	private float endTime;
	
	private boolean locked = false;
	
	private boolean mDisableBounds;//for testing

	private Game mGame;
	
	private GameObject mFocusedObject;
	private GameObject mTempFocusedObject;
	
	private Camera(){};

	private static class CameraHolder {
		public static final Camera instance = new Camera();
	}

	public static Camera getInstance(){
		return CameraHolder.instance;
	}
	
	public void load(Game game){
		mGame   = game;
		mLeft   = 0;
		mBottom = 0;
		mScale  = 1;

		mLeftBound   = 0;
		mBottomBound = 0;
		
		mFocusedObject = null;

		mRightBound    = mGame.getMap().getSize() - GL_WIDTH;
		mTopBound      = GameConVars.MAP_WORLD_HEIGHT - GL_HEIGHT;
		mDisableBounds = false;
	}

	public void setScreenSize(int width, int height){
		SCREEN_WIDTH  = width;
		SCREEN_HEIGHT = height;
		SCREEN_RATIO  = (float)width / (float)height;
	}

	public float getWorldX(float screenX) { 
		return (mLeft - mLeftBound) + (screenX * (GL_WIDTH / SCREEN_WIDTH) / (mScale));
	}

	public float getWorldY(float screenY) { 
		return (mBottom - mBottomBound) + ((SCREEN_HEIGHT-screenY) * (GL_HEIGHT / SCREEN_HEIGHT) / (mScale));
	}
	
	public float getGLX(float screenX){
		return screenX * (GL_WIDTH / SCREEN_WIDTH);
	}
	
	public float getGLY(float screenY){
		return (SCREEN_HEIGHT-screenY) * (GL_HEIGHT / SCREEN_HEIGHT);
	}
	
	public void center(PointF location){
		mLeft   = location.x - (GL_WIDTH/2);
		mBottom = location.y - (GL_HEIGHT/2);
		checkBounds();
	}

	public void move(float deltaX, float deltaY){
		mLeft   -= deltaX;
		mBottom -= deltaY;
		checkBounds();
	}
	
	public void zoom(float scale, PointF center){	
		mScale *= scale;
		if(!mDisableBounds){
			if(mScale > MAXIMUM_VIEW) mScale = MAXIMUM_VIEW;
			if(mScale < MINIMUM_VIEW) mScale = MINIMUM_VIEW;
		}
		resizeBounds();
		
		if(mGame.getCurrentUnit() != null)
			setFocusedObject(mGame.getCurrentUnit());

		mLeft   = mLeft + ((getWorldX(center.x) - mLeft)-(getWorldX(center.x)-mLeft));
		mBottom = mBottom + ((getWorldY(center.y) - mBottom)-(getWorldY(center.y)-mBottom));
		
		checkBounds(); 
	}
	
	public void zoomStandard(){
		mScale = 1.0f;
		resizeBounds();
		checkBounds();
	}
	
	private void resizeBounds(){	
		mLeftBound   = GL_WIDTH/2/mScale - (GL_WIDTH/2);
		mBottomBound = GL_HEIGHT/2/mScale - (GL_HEIGHT/2);
		
		mRightBound = mGame.getMap().getSize() - GL_WIDTH/2/mScale - (GL_WIDTH/2);
		mTopBound   =  GameConVars.MAP_WORLD_HEIGHT - GL_HEIGHT/2/mScale - (GL_HEIGHT/2);
	}
	
	private void checkBounds(){
		if(!mDisableBounds){
			if(mLeft < mLeftBound)  mLeft = mLeftBound;
			if(mLeft > mRightBound) mLeft = mRightBound;
	
			if(mBottom < mBottomBound) mBottom = mBottomBound;
			if(mBottom > mTopBound)    mBottom = mTopBound;
		}
	}
	
	public float getScale(){ return mScale;	}
	
	public float getLeft(){	return mLeft; }
	
	public float getBottom(){ return mBottom; }
	
	public float getLeftPerc() { return mRightBound-mLeftBound == 0 ? 0 : (mLeft-mLeftBound) / (mRightBound-mLeftBound); }
	
	public float getBottomPerc() { return mTopBound-mBottomBound == 0 ? 0 : (mBottom-mBottomBound) / (mTopBound-mBottomBound); }
	
	public GameObject getFocusedObject() { return mFocusedObject; }
	
	public void setFocusedObject(GameObject focusedObject) { 
		if(focusedObject != null){
			mFocusedObject = focusedObject; 
			center(mFocusedObject.getLocation());
		}
	}
	
	public void clearFocusedObject(){ mFocusedObject = null; }
	
	public boolean toggleBounds(){ mDisableBounds = !mDisableBounds; return mDisableBounds; }
	
	public void disableBounds(boolean disable){ mDisableBounds = disable; }
	
	public void setProjectileFocus(Projectile p){
		if(!(mFocusedObject instanceof Projectile)){
			mTempFocusedObject = mFocusedObject;
		}
		mFocusedObject = p;
	}
	
	public void clearProjectileFocus(){
		if(mTempFocusedObject != null){
			PointF tempLoc = mFocusedObject.getLocation();
			setFocusedObject(mTempFocusedObject);
			if(checkLock()){
				lockFor(GameConVars.COLLISION_WAIT, tempLoc);
			}
			mTempFocusedObject = null;
		}
	}
	
	public void lockFor(float time, PointF location){
		if(endTime == 0.0f && checkLock()){//TODO:  this stopped working, find out why!
			center(location);
			locked = true;
			endTime = GameClock.getInstance().getTime() + time;
		}
	}
	
	public boolean checkLock(){
		if(endTime == 0.0f)
			return true;
		if(endTime < GameClock.getInstance().getTime()){
			if(mFocusedObject != null) center(mFocusedObject.getLocation());
			locked = false;
			endTime = 0.0f;
			return true;
		}
		return false;
	}
	
	public void setGLWIDTH(float w){ GL_WIDTH = w; }
	public void setGLHEIGHT(float h){ GL_HEIGHT = h; }
	
}